public class BoundedBufferImpl {

	private Object[] elements;
	private int insertPtr = -1; // circular indices
	private int removePtr = -1;
	private int bufferSize; // size of buffer
	private int usedSlots = 0; // length
	private int emptySlots; // capacity - length
	private static final int defaultBufferSize = 25; // default size

	/**
	 * create a bounded buffer that can hold "size" elements
	 */
	public BoundedBufferImpl(int size) {
		if (size <= 0) // if illegal size, use default
			size = defaultBufferSize;
		elements = new Object[size];
		emptySlots = size;
		bufferSize = size;
	}

	/**
	 * create a bounded buffer of a default size
	 */
	public BoundedBufferImpl() {
		this(defaultBufferSize);
	}

	/**
	 * method to insert "o" at the end of the buffer if nor room, must wait
	 * until another thread has invoked remove()
	 * 
	 * @param o
	 *            Object to insert
	 */
	public synchronized void insert(Object o) {
		try{
			while(emptySlots == 0){
				System.out.format("\nBuffer is full - Prod %s waiting\n", Thread.currentThread().getName());
				wait();
			}
			insertPtr = (insertPtr + 1) % bufferSize;
			elements[insertPtr] = o;
			emptySlots--;
			usedSlots++;
			notifyAll();
		} catch (InterruptedException e){
			Thread.currentThread().interrupt();
		}
	}

	/**
	 * method to remove "o" from the beginning of the buffer if empty, must wait
	 * until another thread has invoked insert()
	 * 
	 * @result Object removed
	 */
	public synchronized Object remove() {
		try{
			while (usedSlots == 0){
				System.out.format("\nBuffer is empty - Cons %s waiting\n",Thread.currentThread().getName());
				wait();
			}
			removePtr = (removePtr + 1) % bufferSize;
			Object o = elements[removePtr];
			emptySlots++;
			usedSlots--;
			notifyAll();
			return o;
		} catch(InterruptedException e){
			Thread.currentThread().interrupt();
		}
		return null;
	}

}